home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2001 / MacHack 2001.toast / pc / The Hacks / Unprotected Memory / MyProcessWorker.m < prev    next >
Encoding:
Text File  |  2001-06-23  |  5.2 KB  |  214 lines

  1. #import "MyProcessWorker.h"
  2.  
  3. #include <mach/mach_types.h>
  4. #include <mach/mach_traps.h>
  5. #include <mach/mach_error.h>
  6.  
  7. #include <sys/sysctl.h>
  8. #include <sys/reboot.h>
  9. #include <unistd.h>
  10.  
  11. @interface MyProcessWorker(PrivateMethods)
  12.  
  13. - (void)notifyCrash;
  14.  
  15. @end
  16.  
  17. @implementation MyProcessWorker
  18.  
  19. - (id)init
  20. {
  21.     if (self = [super init])
  22.     {
  23.         [self walkProcessList];
  24.     }
  25.     
  26.     return self;
  27. }
  28.  
  29. - (void)walkProcessList
  30. {
  31.     int                    mib[4];
  32.     int                 i;
  33.     size_t                 bufSize;
  34.     int                 nentries;
  35.     struct kinfo_proc     *kprocbuf = NULL, *kp;
  36.     BOOL                initialWalk = (myLastSeenPID == 0);
  37.     
  38.     NS_DURING
  39.     {
  40.         mib[0] = CTL_KERN;
  41.         mib[1] = KERN_PROC;
  42.         mib[2] = KERN_PROC_ALL;
  43.         mib[3] = 0;
  44.     
  45.         if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0)
  46.         {
  47.             [NSException raise:NSInternalInconsistencyException
  48.                         format:@"unable to count processes (sysctl)"];
  49.         }
  50.         
  51.         nentries = bufSize / sizeof(struct kinfo_proc);
  52.         kprocbuf = kp = (struct kinfo_proc *)NSZoneMalloc(NULL, bufSize);
  53.  
  54.         if (sysctl(mib, 4, kp, &bufSize, NULL, 0) < 0)
  55.         {
  56.             [NSException raise:NSInternalInconsistencyException
  57.                         format:@"unable to get process list (sysctl)"];
  58.         }   
  59.         
  60.         // it lies the first time, fortunately with a buffer that is too big
  61.         nentries = bufSize / sizeof(struct kinfo_proc);
  62.     
  63.         if (kp != NULL)
  64.         {
  65.             for(i=nentries; --i>=0; ++kp)
  66.             {
  67.                 if (kp->kp_proc.p_pid > myLastSeenPID)
  68.                 {
  69.                     myLastSeenPID = kp->kp_proc.p_pid;
  70.  
  71.                     if ( ! initialWalk)
  72.                     {
  73.                         [NSThread detachNewThreadSelector:@selector(myThreadMain:)
  74.                             toTarget:self withObject:[NSNumber numberWithInt:myLastSeenPID]];
  75.                     }
  76.                 }
  77.             }
  78.         }
  79.     }
  80.     NS_HANDLER
  81.     {
  82.         NSLog(@"Couldn't install exception handler for pid: %d; Exception :%@", [localException description]);
  83.     }
  84.     NS_ENDHANDLER;
  85.     
  86.     if (kprocbuf != NULL)
  87.         NSZoneFree(NULL, kprocbuf);
  88. }
  89.  
  90. #define MACH_CHECK_ERROR(name,ret)    \
  91.         if (ret != KERN_SUCCESS) {    \
  92.         mach_error(#name, ret);        \
  93.         exit(1);                    \
  94.  
  95. - (void)myThreadMain:(id)arg
  96. {
  97.     const int             MSG_SIZE = 512;
  98.     id                        pid = [arg retain];
  99.     mach_msg_header_t     *msg, *reply;
  100.     kern_return_t         krc;
  101.     mach_port_t         _exceptionPort;
  102.     task_t                 targetTask;
  103.     
  104.     NS_DURING
  105.     {
  106.         krc = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_exceptionPort);
  107.         if (krc != KERN_SUCCESS)
  108.         {
  109.             [NSException raise: NSInternalInconsistencyException format: @"Unable to create handler port, krc = %d, %s", krc, mach_error_string(krc)];
  110.         }
  111.         
  112.         krc = mach_port_insert_right(mach_task_self(), _exceptionPort, _exceptionPort, MACH_MSG_TYPE_MAKE_SEND);
  113.         if (krc != KERN_SUCCESS)
  114.         {
  115.             [NSException raise: NSInternalInconsistencyException format: @"Unable insert send write into handler port, krc = %d, %s", krc, mach_error_string(krc)];
  116.         }
  117.     
  118.         krc = task_for_pid(mach_task_self(), [pid intValue], &targetTask);
  119.         if (krc != KERN_SUCCESS)
  120.         {
  121.             [NSException raise: NSInternalInconsistencyException format: @"task_for_pid failed, krc = %d, %s", krc, mach_error_string(krc)];
  122.         }
  123.         
  124.         krc = task_set_exception_ports(targetTask, EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC, _exceptionPort, EXCEPTION_DEFAULT, THREAD_STATE_NONE);
  125.         if (krc != KERN_SUCCESS)
  126.         {
  127.             [NSException raise: NSInternalInconsistencyException format: @"task_set_exception_ports failed, krc = %d, %s", krc, mach_error_string(krc)];
  128.         }
  129.     
  130.         msg = alloca(MSG_SIZE);
  131.         reply = alloca(MSG_SIZE);
  132.  
  133.         while (TRUE)
  134.         {
  135.             NSAutoreleasePool    *pool = [[NSAutoreleasePool alloc] init];
  136.             
  137.             // we only get what we consider to be fatal messages,
  138.             // so just shoot the process and do our thing
  139.  
  140.             krc = mach_msg(msg, MACH_RCV_MSG, MSG_SIZE, MSG_SIZE, _exceptionPort, 0, MACH_PORT_NULL);
  141.             if (krc == KERN_SUCCESS)
  142.             {
  143.                 kill([pid intValue], SIGKILL);
  144.                 [self notifyCrash];
  145.             }
  146.  
  147.             [pool release];
  148.         }
  149.     }
  150.     NS_HANDLER
  151.     {
  152.     
  153.     }
  154.     NS_ENDHANDLER;
  155. }
  156.  
  157. @end
  158.  
  159. @implementation MyProcessWorker(PrivateMethods)
  160.  
  161. - (void)notifyCrash
  162. {
  163.     const int         FREE_CRASH_LIMIT = 1;
  164.     CFStringRef     APPLICATION_PREF_ID = CFSTR("Unprotected Memory");
  165.     CFStringRef     ALREADY_REBOOTED_KEY = CFSTR("AlreadyRebooted");
  166.     
  167.     BOOL            noGrace = NO;
  168.     CFTimeInterval    timeout = 5;
  169.  
  170.     myCrashCount++;
  171.     
  172.     if ( CFPreferencesGetAppBooleanValue(ALREADY_REBOOTED_KEY, APPLICATION_PREF_ID, NULL) )
  173.     {
  174.         noGrace = YES;
  175.         timeout = 0.5;
  176.     }
  177.     
  178.     if (noGrace || (myCrashCount > FREE_CRASH_LIMIT))
  179.     {
  180.         SInt32            response;
  181.         CFOptionFlags    responseFlags;
  182.     
  183.         response = CFUserNotificationDisplayAlert(timeout, 0L, NULL, NULL, NULL,
  184.                                                     CFSTR("An application has unexpectedly quit."),
  185.                                                     CFSTR("You have 5 seconds to save all your work before the machine reboots."),
  186.                                                     CFSTR("Restart"),
  187.                                                     NULL,
  188.                                                     NULL,
  189.                                                     &responseFlags);
  190.                                                     
  191.         CFPreferencesSetAppValue(ALREADY_REBOOTED_KEY, kCFBooleanTrue, APPLICATION_PREF_ID);
  192.         CFPreferencesAppSynchronize(APPLICATION_PREF_ID);
  193.  
  194.                                                     
  195.         if (0 != reboot(RB_AUTOBOOT))
  196.         {
  197.             // call only returns if it could fail, otherwise
  198.             // the machine is rebooting
  199.             
  200.             NSLog(@"reboot didn't work, did you run it as root dummy?");
  201.         }
  202.     }
  203.     else
  204.     {
  205.         (void)CFUserNotificationDisplayNotice(0, 0L, NULL, NULL, NULL,
  206.                                               CFSTR("An application has unexpectedly quit."),
  207.                                               CFSTR("Aren't you glad you didn't have to reboot your machine?"),
  208.                                               CFSTR("Whew!"));
  209.     }
  210. }
  211.  
  212. @end
  213.